package ca.usask.cs.drawTree;

//import Node;

import java.util.HashMap;
import java.util.Iterator;

import org.w3c.dom.Element;

import ca.usask.cs.decisiontree.DecisionTree;
import ca.usask.cs.decisiontree.api.EventNodeInterface;
import ca.usask.cs.decisiontree.api.Edge;
import ca.usask.cs.decisiontree.api.IntermediateTreeNode;
import ca.usask.cs.decisiontree.api.TerminalNodeInterface;
import ca.usask.cs.decisiontree.api.TreeNode;

import com.nootropic.processing.layers.AppletLayers;
import processing.core.*;

public class DrawControler extends PApplet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 924234679836690281L;

	public static HashMap<Integer, TheNode> nodes = new HashMap<Integer, TheNode>();
	public static HashMap<Integer, TheEdge> edges = new HashMap<Integer, TheEdge>();

	public TheNode parenttheNode;
	public int Ncount;
	public int Ecount;
	public TheNode roottheNode;
	public boolean init = false;
	public static int type;
	public float sval = 1;
	public static float rootx = 0;
	public float rooty;
	public static TheNode selection;
	public static TheNode checkOverNode;
	public static String TreeName;
	public static float dBranchTime;
	public static float cBranchTime;
	public float r = 25;
	public static int layer;
	public int eachLayerNumberofEdges;
	public static int DecisionLayerNumberofEdges;
	public static int ChanceLayerNumberofEdges;
	
	public float distanceForEachLayer = 100;
	// public float distanceForEachLayerRate = 10;
	public float reduceRate = (float) 0.8;
	AppletLayers layers;
	public boolean HbarSeleted = false;
	public boolean VbarSeleted = false;
	float YdistanceConst = sqrt((float) 5.5);
	int transparancySpeed = 2;
	final int mouseEffectColor = 0xffFF0000;
	int rootID = 2;
	int NextEdgeID = 1000;

	public static DecisionTree CopyTree;
	@SuppressWarnings("unchecked")
	public static HashMap CopyFromXMLtreeNodeList = new HashMap();
 @SuppressWarnings("deprecation")
	public void setup() {
		size(screen.width, screen.height); // Size must be the first statement
		smooth();
		rooty = height / 2;
		layers = new AppletLayers(this);
		HorizontalScrollbar HBar = new HorizontalScrollbar(0, 5, width - 10,
				15, 3 * 1 + 2, this);
		VerticalScrollbar vBar = new VerticalScrollbar(width - 20, height / 2,
				20, height, 3 * 1 + 2, this);
		TimeRuler ruler = new TimeRuler(width, height, this,HBar);
		MsgWindow MsgWindow = new MsgWindow(this);

		layers.addLayer(ruler);
		layers.addLayer(HBar);
		layers.addLayer(vBar);

		layers.addLayer(MsgWindow);

		trimDistanceForEachLayer();

	}

	public void draw() {
		background(255); // Set the background to black
		System.out.println("the size of list is " + CopyTree.getTreeID());
		scale(sval, sval);
		update();
		mouseOverChangeColor();
	//	drawRulerLines();
		zoomInOut();
	}

	public void update() {
		if (!init) {
			initCreateTree();
			init = true;

		} else {
			updatedrawtheNodes();
			updatedrawEdges();
		}
	}

	private void mouseOverChangeColor() {
		if (mouseOver()) {
			checkOverNode.changeColor(mouseEffectColor);
		}
	}

	private void zoomInOut() {
		if (mousePressed && mouseButton == RIGHT) {
			if (pmouseX - mouseX > 0) {
				sval = (float) (sval - 0.05);
			} else if (pmouseX - mouseX < 0) {
				sval = (float) (sval + 0.05);
			}
		}
	}

	private void trimDistanceForEachLayer() {
		float averageLayerDistance = dBranchTime + cBranchTime;
		float totalDistance = averageLayerDistance * layer;
		float rateForDistance = screen.width / totalDistance;

		dBranchTime = dBranchTime * rateForDistance;
		cBranchTime = cBranchTime * rateForDistance;

	}

	protected void paint() {
		if (layers != null) {
			layers.paint(this);
		} else {
			super.paint();
		}
	}

	void initCreateTree() {
		Ncount = 0;
		Ecount = 0;
		NextEdgeID = 1000;
		drawRootNode(rootx, rooty, r);
		
		if (roottheNode.Ndtype % 2 == 0) {
			eachLayerNumberofEdges = DecisionLayerNumberofEdges;
		} else if (roottheNode.Ndtype % 2 == 1) {
			eachLayerNumberofEdges = ChanceLayerNumberofEdges;
		}

		drawNodes(layer, eachLayerNumberofEdges, rootID, rootx, rooty, r
				* reduceRate, YdistanceConst, roottheNode, type + 1);
		writeBasicDecisionTree();
	}

	int drawNodes(int layer, int eachLayerNumberofEdges, int NodeID, float x,
			float y, float r, float YdistanceConst, TheNode parenttheNode,
			int theNodetype) {
		if (layer <= 1) {
			theNodetype = -1;
		}

		if (parenttheNode.Ndtype % 2 == 0) {
			eachLayerNumberofEdges = DecisionLayerNumberofEdges;
		} else if (parenttheNode.Ndtype % 2 == 1) {
			eachLayerNumberofEdges = ChanceLayerNumberofEdges;
		}

		float nodesYdistance = (2 * YdistanceConst * distanceForEachLayer)
				/ (eachLayerNumberofEdges - 1);
		float nextLayerX = x + distanceForEachLayer;
		float nextLayerY = y - YdistanceConst * distanceForEachLayer;

		for (int i = 0; i < eachLayerNumberofEdges; i++) {
			// EdgeID++;
			TheNode ntemp = createNode(nextLayerX, nextLayerY, r, theNodetype);
			nodes.put(++NodeID, ntemp);
			ntemp.setTreeNodeID(NodeID);
			drawNode(ntemp);
			TheEdge etemp = new TheEdge(this, parenttheNode, ntemp);
			edges.put(NextEdgeID++, etemp);
			System.out.println(NextEdgeID + "");
			etemp.drawEdge();
			// int tempEdgeID = EdgeID;
			// System.out.println("tempEdgeID "+tempEdgeID+"");
			if (layer > 1) {
				NodeID = drawNodes(layer - 1, eachLayerNumberofEdges, NodeID,
						nextLayerX, nextLayerY, r * reduceRate, YdistanceConst
								* (float) 0.4, ntemp, theNodetype + 1);
			}

			nextLayerY = nextLayerY + nodesYdistance;
		}
		System.out.println(NodeID + "");
		return NodeID;

	}

	void drawRootNode(float x, float y, float r) {

		roottheNode = createNode(x, y, r, type);
		nodes.put(rootID, roottheNode);
		roottheNode.setTreeNodeID(rootID);
		drawNode(roottheNode);
		parenttheNode = roottheNode;
	}

	public TheNode createNode(float x, float y, float r, int NodeType) {
		TheNode node;
		switch (NodeType % 2) {
		case 0:
			node = new DecisionNode(this, x, y, r, NodeType);
			distanceForEachLayer = dBranchTime;
			break;
		case 1:

			node = new ChanceNode(this, x, y, r, NodeType);// chance Node
			distanceForEachLayer = cBranchTime;
			break;
		case -1:
			node = new TerNode(this, x, y, r, NodeType); // Terminal Node
		default:
			node = new TerNode(this, x, y, r, NodeType);// Terminal Node
		}
		return node;
	}

	void drawNode(TheNode n) {
		switch (n.Ndtype % 2) {
		case 0:
			n.draw(n.x, n.y, n.r); // decision Node
			n.colorBack();
			break;
		case 1:

			n.draw(n.x, n.y, n.r); // chance Node
			n.colorBack();
			break;
		case -1:
			n.draw(n.x, n.y); // Terminal Node
			n.colorBack();
			break;
		default:
			break;
		}
	}
 @SuppressWarnings("unchecked")
	public void mousePressed() {
		if (mouseButton == LEFT) {

			float closest;
			Iterator iterator = nodes.keySet().iterator();

			while (iterator.hasNext()) {
				TheNode n = nodes.get(iterator.next());
				closest = n.r;
				float d = dist(mouseX / sval, mouseY / sval, n.x, n.y);
				if (d < closest) {
					selection = n;
					closest = d;
				}
			}

			while (iterator.hasNext()) {
				TheNode n = nodes.get(iterator.next());
				closest = n.r;
				float d = dist(mouseX / sval, mouseY / sval, n.x, n.y);
				if (d < closest) {

					selection = n;
					closest = d;
				}
			}

			// release mouse button
			if (selection != null) {

				if (mouseButton == LEFT) {
					selection.fixed = true;
					selection.changeColor(mouseEffectColor);

				}
			}
		}
	}
 @SuppressWarnings("unchecked")
	public boolean mouseOver() {
		Iterator iterator = nodes.keySet().iterator();
		if (!mousePressed) {
			float closest;
			while (iterator.hasNext()) {
				TheNode n = nodes.get(iterator.next());
				closest = n.r;
				float d = dist(mouseX / sval, mouseY / sval, n.x, n.y);
				if (d < closest) {
					checkOverNode = n;
					closest = d;
				}
			}
		}
		// release mouse button
		if (checkOverNode != null) {

			return true;
		} else {
			return false;

		}

	}

	public void mouseDragged() {
		if (selection != null && mouseButton == LEFT) {
			selection.y = mouseY / sval;
			selection.changeColor(mouseEffectColor);
		}
	}
 @SuppressWarnings("unchecked")
	void updatedrawtheNodes() {
		Iterator iterator = nodes.keySet().iterator();
		while (iterator.hasNext()) {
			drawNode(nodes.get(iterator.next()));
		}
	}
 @SuppressWarnings("unchecked")
	void VerticalScrollBarUpdate(float offsetY) {
		Iterator iterator = nodes.keySet().iterator();
		while (iterator.hasNext()) {
			// drawNode(nodes.get(iterator.next()));
			(nodes.get(iterator.next())).y += offsetY / sval;
		}
	}
 @SuppressWarnings("unchecked")
	void HorizontalScrollBarUpdate(float offsetY) {
		Iterator iterator = nodes.keySet().iterator();
		while (iterator.hasNext()) {
			// drawNode(nodes.get(iterator.next()));
			(nodes.get(iterator.next())).x += offsetY / sval;
		}
	}
 @SuppressWarnings("unchecked")
	void updatedrawEdges() {
		Iterator edgeiterator = edges.keySet().iterator();
		while (edgeiterator.hasNext()) {
			edges.get(edgeiterator.next()).drawEdge();

		}

	}

	public void mouseReleased() {
		selection = null;
	}

	public static void main(String args[]) {
		PApplet.main(new String[] { "--present",
				"ca.usask.ca.drawTree.drawControler" });
	}

	public void writeBasicDecisionTree() {
		DecisionTree tree = CopyTree;
		String treeName = tree.getTreeName();
		int treeID = tree.getTreeID();
		// Element treeEle = CreateBasicDecisionTree(curEle, treeName, treeID);
		writeTreeNode(tree.getRootNode());
	}

	/**
	 * Creates a Basic DecisionTree Element and append it to the curEle
	 * 
	 * @param curEle
	 *            the current element in which the BDTree Element will append to
	 * @param theTreeName
	 *            treeName of the BasicDecisionTree
	 * @param theTreeID
	 *            treeID of the BasicDecisionTree
	 * @return
	 */
	// public Element CreateBasicDecisionTree(Element curEle, String
	// theTreeName, int theTreeID)
	// {
	// Element BDTree = createBDTreeEle(theTreeName,theTreeID);
	// //Append the BDTree to the curEle
	// curEle.appendChild(BDTree);
	// return BDTree;
	// }
	// public Element createBDTreeEle(String theTreeName,int theTreeID){
	// // Element BDTree = dom.createElement("DecisionTree");
	// // BDTree.setAttribute("type", "BasicDecisionTree");
	// //create TreeName element with its appropriate value and append to BDTree
	// // String newTreeID = ""+theTreeID;
	// // createChildren(BDTree,new String[]{"treeName","treeID"},new String[]
	// {theTreeName,newTreeID});
	//	 	
	// // return BDTree;
	// }

	public void writeTreeNode(TreeNode thCurNode) {
		// gather all the information to create the corresponding node type and
		// call the its element creation method
		if (thCurNode.getNodeType().equals("TerminalNode")) {
			int sourceID = -1;
			TerminalNodeInterface curNode = (TerminalNodeInterface) thCurNode;

			// only retrieve sourceNodeID if it is not the rootNode
			if (thCurNode != CopyTree.getRootNode()) {
				sourceID = curNode.getSourceEdge().getEdgeID();
			}

			int treeNodeID = curNode.getTreeNodeID();
			double time = curNode.getTime();
			String varName = curNode.getVariableName();
			double terVal = curNode.getRollBackValue();
			boolean selected = curNode.isSelected();

			CreateTerminalNodeElement(treeNodeID, time, varName, terVal,
					selected, sourceID);
		} else if (thCurNode.getNodeType().equals("ChanceNode")) {
			int sourceID = -1;

			EventNodeInterface curNode = (EventNodeInterface) thCurNode;

			// only retrieve sourceNodeID if it is not the rootNode
			if (thCurNode != CopyTree.getRootNode()) {
				sourceID = curNode.getSourceEdge().getEdgeID();
			}

			int treeNodeID = curNode.getTreeNodeID();
			double time = curNode.getTime();
			String varName = curNode.getVariableName();
			boolean selected = curNode.isSelected();
			double rollBackValue = curNode.getRollBackValue();

			CreateChanceNodeElement(treeNodeID, time, varName, rollBackValue,
					selected, sourceID);

			Iterator<Edge> edgeList = curNode.getChildEdgeList();

			// if there is atleast one edge, append the ChildrenEdgeList element
			// if(edgeList.hasNext())
			// {
			//
			// Element childrenEdgeListEle =
			// CopyTree.createChildrenEdgeList(nodeEle);
			//
			// // for each Edge call writeEdge method
			// while(edgeList.hasNext())
			// {
			// writeEdge(childrenEdgeListEle, edgeList.next());
			// }
			// }
			// edgeList = curNode.getChildEdgeList();

			// if there is atleast one edge, append the ChildList element
			if (edgeList.hasNext()) {

				// no Element childListEle =
				// this.write.createChildList(nodeEle);

				// for each edge call the writeTreeNode with its destinationNode
				while (edgeList.hasNext()) {
					writeTreeNode(edgeList.next().getDestinationNode());
				}
			}
		} else {
			int sourceID = -1;

			IntermediateTreeNode curNode = (IntermediateTreeNode) thCurNode;

			// only retrieve sourceNodeID if it is not the rootNode
			if (thCurNode != CopyTree.getRootNode()) {
				sourceID = curNode.getSourceEdge().getEdgeID();
			}

			int treeNodeID = curNode.getTreeNodeID();
			double time = curNode.getTime();
			String varName = curNode.getVariableName();
			boolean selected = curNode.isSelected();
			double rollBackValue = curNode.getRollBackValue();

			CreateDecisionNodeElement(treeNodeID, time, varName, rollBackValue,
					selected, sourceID);

			Iterator<Edge> edgeList = curNode.getChildEdgeList();

			// if there is atleast one edge, append the ChildrenEdgeList element
			// if(edgeList.hasNext())
			// {
			//
			// // Element childrenEdgeListEle =
			// this.write.createChildrenEdgeList(nodeEle);
			//
			// // for each Edge call writeEdge method
			// while(edgeList.hasNext())
			// {
			// writeEdge(childrenEdgeListEle, edgeList.next());
			// }
			// }
			edgeList = curNode.getChildEdgeList();

			// if there is atleast one edge, append the ChildList element
			if (edgeList.hasNext()) {

				// Element childListEle = this.write.createChildList(nodeEle);

				// for each edge call the writeTreeNode with its destinationNode
				while (edgeList.hasNext()) {
					writeTreeNode(edgeList.next().getDestinationNode());
				}
			}
		}

	}

	public void CreateTerminalNodeElement(int theTreeNodeID, double theTime,
			String theVarName, double theTerVal, boolean theSelected,
			int theSourceID) {
		// Element TNode = dom.createElement("TreeNode");
		// TNode.setAttribute("type", "TerminalNode");

		// create treeNodeID element with its values and append to DNode Element

		TerNode tn = (TerNode) nodes.get(theTreeNodeID);

		if (tn != null) {

			String newTreeNodeID = "" + theTreeNodeID;

			tn.setTime(theTime);

			// String newTime = "" + theTime;
			tn.setVariableName(theVarName);
			// String newVarName = theVarName;

			// createChildren(TNode,new String
			// []{"treeNodeID","time","variableName"} ,new String
			// []{newTreeNodeID,newTime,newVarName});;

			// if there is not terminal value don't output
			if (theTerVal != Double.NaN) {
				// create terVal element with its values and append to DNode
				// Element

				tn.setRollBackValue(theTerVal);
				// String newTerVal = "" + theTerVal;
				// appendChild(TNode,"terminalValue",newTerVal);
			}

			// create selected element with its values and append to DNode
			// Element
			tn.setSelected(theSelected);
			// String newSelected = "" + theSelected;

			// if sourceID == -1 then it must be a root node so omit the
			// sourceID
			// if (theSourceID != -1) {
			// // create sourceID element with its values and append to DNode
			// // Element
			// // tn.s
			// String newSourceID = "" + theSourceID;
			//
			// }
		}

	}

	public void CreateChanceNodeElement(int theTreeNodeID, double theTime,
			String theVarName, double theRollBackValue, boolean theSelected,
			int theSourceID) {
		// create treeNodeID element with its values and append to DNode Element
		ChanceNode cn = (ChanceNode) nodes.get(theTreeNodeID);
		if (cn != null) {
			cn.setTime(theTime);
//			String newTreeNodeID = "" + theTreeNodeID;
//			String newTime = "" + theTime;
			cn.setVariableName(theVarName);
			//String newVarName = theVarName;

			if (theRollBackValue != Double.NaN) {
				// create terVal element with its values and append to DNode
				// Element
				cn.setRollBackValue(theRollBackValue);
			//	String newRollBackValue = "" + theRollBackValue;
			}
			// create selected element with its values and append to DNode
			// Element
			cn.setSelected(theSelected);
		//	String newSelected = "" + theSelected;

			// if sourceID == -1 then it must be a root node so omit the
			// sourceID
//			if (theSourceID != -1) {
//				// create sourceID element with its values and append to DNode
//				// Element
//				String newSourceID = "" + theSourceID;
//			}
		}

	}

	public void CreateDecisionNodeElement(int theTreeNodeID, double theTime,
			String theVarName, double theRollBackValue, boolean theSelected,
			int theSourceID) {
		// Element DNode = dom.createElement("TreeNode");
		// DNode.setAttribute("type", "DecisionNode");
		DecisionNode dn = (DecisionNode) nodes.get(theTreeNodeID);
		// create treeNodeID element with its values and append to DNode Element
		String newTreeNodeID = "" + theTreeNodeID;
		dn.setTime(theTime);
		dn.setVariableName(theVarName);
//		String newTime = "" + theTime;
//		String newVarName = theVarName;

		// createChildren(DNode,new String
		// []{"treeNodeID","time","variableName"} ,new String
		// []{newTreeNodeID,newTime,newVarName});

		if (theRollBackValue != Double.NaN) {
			// create terVal element with its values and append to DNode Element
			
			dn.setRollBackValue(theRollBackValue);
//			String newRollBackValue = "" + theRollBackValue;
			// appendChild(DNode,"rollBackValue",newRollBackValue);
		}

		// create selected element with its values and append to DNode Element
		dn.setSelected(theSelected);
		String newSelected = "" + theSelected;
		// DNodeappendChild(DNode,"selected",newSelected);
		// createChildren(DNode,new String []{"selected"} ,new String
		// []{newSelected});
		// if sourceID == -1 then it must be a root node so omit the sourceID
//		if (theSourceID != -1) {
//			// create sourceID element with its values and append to DNode
//			// Element
//			String newSourceID = "" + theSourceID;
//			// appendChild(DNode,"sourceEdgeID",newSourceID);
//
//		}
		// return DNode;
	}

}
